package sideeffect

import (
	"context"
	"github.com/nuttech/bell"
	"github.com/stretchr/testify/mock"
	"gitverse.ru/synapse/kubelatte/pkg/api/v1alpha1"
	"gitverse.ru/synapse/kubelatte/pkg/kubeapi"
	"gitverse.ru/synapse/kubelatte/pkg/observability/logs"
	"gitverse.ru/synapse/kubelatte/pkg/operator"
	util2 "gitverse.ru/synapse/kubelatte/pkg/operator"
	mocks2 "gitverse.ru/synapse/kubelatte/pkg/operator/controllers/clientset/mocks"
	"gitverse.ru/synapse/kubelatte/pkg/storage"
	"go.uber.org/zap"
	errors2 "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"os"
	"testing"
	"time"
)

func TestMain(m *testing.M) {
	logs.Logger = zap.NewNop()
	code := m.Run()
	os.Exit(code)
}

func TestController_ApplySideEffectCreate(t *testing.T) {
	type args struct {
		info *SE
	}
	tests := []struct {
		name     string
		args     args
		trigger  v1alpha1.Trigger
		template v1alpha1.Template
		setVars  func()
	}{
		{
			name: "err parent publication",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return nil, errors2.NewNotFound(schema.GroupResource{}, "Pod test")
				}
			},
			args: args{
				info: &SE{
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						ParentTag:  0,
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
						TemplateRefs: []string{"templNs/templName"},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "unknown parent publication",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return []byte("kind: Pod"), nil
				}
				operator.TimeoutParentWaiting = 1 * time.Second
				emptyParentUpdateChecker = func(_ []byte, _ int64) parentStatus {
					return unknown
				}
			},
			args: args{
				info: &SE{
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						ParentTag:  0,
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
						TemplateRefs: []string{"templNs/templName"},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "getResourceInfo err",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return []byte("kind: Pod"), nil
				}
				operator.TimeoutParentWaiting = 1 * time.Second
				emptyParentUpdateChecker = func(_ []byte, _ int64) parentStatus {
					return publishedHashChanged
				}
			},
			args: args{
				info: &SE{
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
			},
		},
		{
			name: "ok",
			setVars: func() {
				kubeapi.GetResourceMeta = func(client *kubernetes.Clientset, kind string, version string) *kubeapi.ResourceMeta {
					return &kubeapi.ResourceMeta{
						Name:         "Pod",
						Plural:       "pods",
						Path:         "api",
						Namespaced:   true,
						GroupVersion: "v1",
					}
				}

				kubeapi.GetResource = func(ctx context.Context, resourceMeta *kubeapi.ResourceMeta, resourceName string, namespaceName string) ([]byte, error) {
					return []byte("{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"name\":\"pod-se-plus-mutation\",\"resourceVersion\": \"verse1\",\"uid\": \"31ce6848-bee4-40fb-be85-7f796dd11685\",\"namespace\":\"kate-kblt-dev\",\"labels\":{\"sidecar.istio.io/inject\":\"false\",\"side-effect-plus-mutation\":\"enabled\"},\"annotations\":{\"kblt.annot-ns/merge\":\"enabled\"}},\"spec\":{\"hostNetwork\":false,\"serviceAccount\":\"default\",\"serviceAccountName\":\"default\",\"restartPolicy\":\"Never\",\"automountServiceAccountToken\":false,\"securityContext\":{\"runAsNonRoot\":true,\"seccompProfile\":{\"type\":\"RuntimeDefault\"}},\"containers\":[{\"image\":\"dzo.sw.sbc.space/sbt_dev/ci90000221_fedmesh_dev/federation-demo@sha256:548cc416011c86bb1e2c6d06289aab0771a0f749c044656deaac5fc86d891fea\",\"name\":\"test\",\"imagePullPolicy\":\"Always\",\"securityContext\":{\"allowPrivilegeEscalation\":false,\"capabilities\":{\"drop\":[\"ALL\"]},\"runAsNonRoot\":true,\"privileged\":false}}]}}"), nil
				}
			},
			args: args{
				info: &SE{
					Configs: Configs{
						Kind:       "Pod",
						ApiVersion: "v1",
						Metadata: Metadata{
							Namespace: "kblt-dev",
							Name:      "podik :D ! XD",
						},
						TemplateRefs: []string{"templNs/templName", "fake/ref"},
						TriggerRef: TriggerRef{
							Name:             "triggName",
							Namespace:        "ns",
							SideEffectConfig: "sideEffThis",
						},
					},
				},
			},
			trigger: v1alpha1.Trigger{
				ObjectMeta: metav1.ObjectMeta{Name: "triggName"},
				Spec: v1alpha1.TriggerSpec{
					SideEffectConfigs: []v1alpha1.SideEffectConfig{{
						Name:         "sideEffThis",
						TemplateRefs: []string{"templNs/templName"},
					}}},
			},
			template: v1alpha1.Template{
				ObjectMeta: metav1.ObjectMeta{
					Namespace: "templNs",
					Name:      "templName",
				},
				Spec: v1alpha1.TemplateSpec{
					Data: "---\nkind: TriggerInstance\napiVersion: kubelatte.synapse.sber/v1alpha1\nmetadata:\n  name: trigger-instance-se-plus-mutation\n  annotations:\n    kblt/side-effect-plus-mutation: enabled\nspec:\n  data: data\n",
				},
			},
		},
	}

	t.Run("disabled kbltSideEffect", func(t *testing.T) {
		util2.KbltSideEffect = "disabled"
		(&Executor{}).ApplySideEffect(context.Background(), nil)
	})

	util2.KbltSideEffect = "enabled"
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.setVars()

			triggerInstanceClient := mocks2.NewTriggerInstanceInterface(t)
			triggerInstanceClient.On("Get", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, errors2.NewNotFound(schema.GroupResource{}, "ti")).Maybe()
			triggerInstanceClient.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil).Maybe()

			kbtlKubeClientMock := mocks2.NewV1Alpha1Interface(t)
			kbtlKubeClientMock.On("TriggerInstance").Return(triggerInstanceClient).Maybe()

			storage.Storage = &storage.StorageController{}
			storage.Storage.Start(false, false)
			storage.Storage.UpdateTemplate(bell.Message{Value: tt.template})
			storage.Storage.UpdateTrigger(bell.Message{Value: tt.trigger})

			c := Executor{kbtlKubeClient: kbtlKubeClientMock}
			_ = c.ApplySideEffect(context.Background(), tt.args.info)
		})
	}
}
